package org.qianji.goosql.dql.clause;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.qianji.goosql.Fragment;
import org.qianji.goosql.cond.And;
import org.qianji.goosql.cond.Condition;
import org.qianji.goosql.field.Field;

/**
 * 分组子句。
 * 
 * @author gmz
 * 
 */
public class GroupBy implements Fragment {

	/**
	 * 
	 */
	private static final long serialVersionUID = 5123536271068995269L;

	private List<Field> groupFields;
	private Condition having;

	/**
	 * 默认构造子。
	 */
	public GroupBy() {
		super();
	}

	/**
	 * 根据分组字段构造分组子句。
	 * 
	 * @param groupFields
	 *            {@link List List&lt;Field&gt;} 分组字段
	 */
	public GroupBy(List<Field> groupFields) {
		super();
		this.groupFields = groupFields;
	}

	/**
	 * 根据分组字段、分组条件构造分组子句。
	 * 
	 * @param groupFields
	 *            {@link List List&lt;Field&gt;} 分组字段
	 * @param having
	 *            {@link Condition} 分组条件
	 */
	public GroupBy(List<Field> groupFields, Condition having) {
		super();
		this.groupFields = groupFields;
		this.having = having;
	}

	/**
	 * 添加分组字段。
	 * 
	 * @param field
	 *            {@link Field} 分组字段
	 * @return {@link GroupBy} 分组子句
	 */
	public GroupBy addGroupField(Field field) {
		if (field == null) {
			throw new NullPointerException("field can't be null");
		}
		if (groupFields == null) {
			groupFields = new ArrayList<Field>();
		}
		if (!groupFields.contains(field)) {
			groupFields.add(field);
		}
		return this;
	}

	/**
	 * 获取分组字段。
	 * 
	 * @return {@link List List&lt;Field&gt;} 分组字段
	 */
	public List<Field> getGroupFields() {
		return groupFields;
	}

	/**
	 * 设置分组字段。
	 * 
	 * @param groupFields
	 *            {@link List List&lt;Field&gt;} 分组字段
	 * @return {@link GroupBy} 分组子句
	 */
	public GroupBy setGroupFields(List<Field> groupFields) {
		this.groupFields = groupFields;
		return this;
	}

	/**
	 * 添加分组条件。
	 * 
	 * @param cond
	 *            {@link Condition} 分组条件
	 * @return {@link GroupBy} 分组子句
	 */
	public GroupBy addHaving(Condition cond) {
		if (cond == null) {
			throw new NullPointerException("cond can't be null");
		}
		having = (having != null ? new And(having, cond) : cond);
		return this;
	}

	/**
	 * 获取分组条件。
	 * 
	 * @return {@link Condition} 分组条件
	 */
	public Condition getHaving() {
		return having;
	}

	/**
	 * 设置分组条件。
	 * 
	 * @param having
	 *            {@link Condition} 分组条件
	 * @return {@link GroupBy} 分组子句
	 */
	public GroupBy setHaving(Condition having) {
		this.having = having;
		return this;
	}

	/**
	 * 获取分组字段数。
	 * 
	 * @return {@link java.lang.Integer int} 分组字段数
	 */
	public int size() {
		return (groupFields != null ? groupFields.size() : 0);
	}

	@Override
	public String toSql() {
		StringBuffer gb = new StringBuffer();
		if (groupFields != null || !groupFields.isEmpty()) {
			gb.append("GROUP BY ").append(groupFields.get(0).toSql());
			int size = size();
			for (int i = 1; i < size; i++) {
				gb.append(",").append(groupFields.get(i).toSql());
			}
			if (having != null) {
				gb.append(" ").append(having.toSql());
			}
		}
		return gb.toString();
	}

	@Override
	public List<Object> getPlacedParameters() {
		List<Object> ppl = new ArrayList<Object>();
		if (groupFields != null || !groupFields.isEmpty()) {
			for (Field fld : groupFields) {
				ppl.addAll(fld.getPlacedParameters());
			}
			if (having != null) {
				ppl.addAll(having.getPlacedParameters());
			}
		}
		return ppl;
	}

	@Override
	public Map<String, Object> getNamedParameters() {
		Map<String, Object> npm = new HashMap<String, Object>();
		if (groupFields != null || !groupFields.isEmpty()) {
			for (Field fld : groupFields) {
				npm.putAll(fld.getNamedParameters());
			}
			if (having != null) {
				npm.putAll(having.getNamedParameters());
			}
		}
		return npm;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null || !(obj instanceof GroupBy)) {
			return false;
		}
		GroupBy other = (GroupBy) obj;
		if (size() == other.size()
				&& ((having == null && other.getHaving() == null) || having.equals(other.getHaving()))) {
			for (Field fld : other.getGroupFields()) {
				if (!groupFields.contains(fld)) {
					return false;
				}
			}
			return true;
		}
		return false;
	}

	@Override
	public int hashCode() {
		int hc = 37 * 17 + (having != null ? having.hashCode() : 0);
		if (size() > 0) {
			for (Field fld : groupFields) {
				hc = 37 * hc + fld.hashCode();
			}
		}
		return hc;
	}

	@Override
	public String toString() {
		return toSql();
	}

}
